#pragma once
#include <iostream>
#include <sys/select.h>
#include <string>
#include <unordered_map>
#include "Sock.hpp"
#include "Log.hpp"

const static uint16_t gport = 8888;

#define READ_EVENT (0x1)
#define WRITE_EVENT (0x1 << 1)
#define EXCEPT_EVENT (0x1 << 2)

typedef struct FdEvent
{
    int fd;
    uint8_t event;
    std::string clientip;
    uint16_t clientport;
} type_t;

static const uint16_t defaultevent = 0;

class SelectServer
{

    static const int N = sizeof(fd_set) * 8; // 用来管理fd的数组大小

public:
    SelectServer(uint16_t port = gport) : _port(port)
    {
        _dictionary = {{"Hello", "你好"},
                       {"hello", "你好"}};
    }

    void InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();

        for (int i = 0; i < N; ++i)
        {
            _fdarray[i].fd = defaultfd;
            _fdarray[i].event = defaultevent;
            _fdarray[i].clientport = 0;
        }
    }

    void Accepter()
    {
        // std::cout << "有一个新连接到来了" << std::endl;
        //  这个时候再Accept
        std::string clientip;
        uint16_t clientport;
        int sock = _listensock.Accept(&clientip, &clientport);
        if (sock < 0)
        {
            return;
        }
        logMessage(Debug, "[%s:%d], sock: %d", clientip.c_str(), clientport, sock);
        // 同样的我们这时候不能直接对fd直接进行read/recv，因为我们并不清楚此时fd当中是否有数据就绪
        // 因此也是用同样方法将其交给select帮我们等待事件就绪
        int pos = 1;
        for (; pos < N; ++pos)
        {
            if (_fdarray[pos].fd == defaultfd) // 找到没被占用的位置
            {
                break;
            }
        }
        if (pos >= N) // 判断其是否合法
        {
            close(sock); // 不合法（越界）就代表管理fd的数组已满，无法继续承载，只能将此fd关闭
            logMessage(Warning, "sockfd array[] full");
        }
        else
        {
            _fdarray[pos].fd = sock; // 合法就写入管理fd的数组
            _fdarray[pos].event = READ_EVENT;
            _fdarray[pos].clientip = clientip;
            _fdarray[pos].clientport = clientport;
        }
    }

    void Recver(int index)
    {
        // serviceIO()
        int fd = _fdarray[index].fd;
        char buffer[1024];
        ssize_t s = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (s > 0)
        {
            buffer[s - 2] = '\0';
            // std::cout << _fdarray[index].clientip << ":" << _fdarray[index].clientport << "# " << buffer << std::endl;
            //  发送回去时也要将fd交给select等待写事件就绪
            //  std::string echo = buffer;
            //  echo += "[select server echo]";
            std::string echo = _dictionary[buffer];
            send(fd, echo.c_str(), echo.size(), 0);
        }
        else
        {
            if (s == 0)
            {
                logMessage(Info, "client quit..., fdarray[%d] -> defaultfd: %d->%d", index, fd, defaultfd);
            }
            else
            {
                logMessage(Info, "recv error, client quit..., fdarray[%d] -> defaultfd: %d->%d", index, fd, defaultfd);
            }
            close(fd);
            _fdarray[index].fd = defaultfd;
            _fdarray[index].event = defaultevent;
            _fdarray[index].clientip.resize(0);
            _fdarray[index].clientport = 0;
        }
    }

    void HandlerEvent(fd_set &rfds, fd_set &wfds)
    {
        for (int i = 0; i < N; ++i)
        {
            if (_fdarray[i].fd == defaultfd)
            {
                continue;
            }
            if ((_fdarray[i].event & READ_EVENT) && (FD_ISSET(_fdarray[i].fd, &rfds))) // 如果关心该fd的读事件
            {
                if (_fdarray[i].fd == _listensock.Fd()) // 如果该fd是_listensock
                {
                    Accepter();
                }
                else if (_fdarray[i].fd != _listensock.Fd()) // 如果该fd并不是_listensock
                {
                    Recver(i);
                }
            }
            else if ((_fdarray[i].event & WRITE_EVENT) && (FD_ISSET(_fdarray[i].fd, &wfds))) // 如果关心该fd的写事件
            {
                // TODO
            }
            else
            {
            }
        }
    }

    void Start()
    {
        // _listensock.Accept();
        // 要实现多路复用我们不能直接Accept，一开始服务器里没有新连接到来时Accept会发生阻塞式等待
        // 在网络中，新连接到来被当做读事件就绪
        // 我们需要select来帮我们等待读事件就绪
        // demo0
        _fdarray[0].fd = _listensock.Fd();
        _fdarray[0].event = READ_EVENT;
        while (true)
        {
            fd_set rfds;
            fd_set wfds;
            FD_ZERO(&rfds);
            FD_ZERO(&wfds);
            int maxfd = _fdarray[0].fd;
            for (int i = 0; i < N; ++i)
            {
                if (_fdarray[i].fd == defaultfd) // 过滤非法fd
                {
                    continue;
                }
                // 合法fd
                if (_fdarray[i].event & READ_EVENT) // 如果关心该fd的读事件
                {
                    FD_SET(_fdarray[i].fd, &rfds);
                }
                if (_fdarray[i].event & WRITE_EVENT) // 如果关心该fd的写事件
                {
                    FD_SET(_fdarray[i].fd, &wfds);
                }
                if (maxfd < _fdarray[i].fd)
                {
                    maxfd = _fdarray[i].fd; // 更新最大fd
                }
            }
            // struct timeval timeout = {0, 0};
            int n = select(maxfd + 1, &rfds, &wfds, nullptr, nullptr);
            switch (n)
            {
            case 0:
                logMessage(Debug, "timeout %d: %s", errno, strerror(errno));
                break;
            case -1:
                logMessage(Warning, "%d: %s", errno, strerror(errno));
                break;
            default:
                logMessage(Debug, "有一个事件就绪: %d", n);
                HandlerEvent(rfds, wfds);
                DebugPrint();
                break;
            }
        }
    }

    void DebugPrint()
    {
        std::cout << "fdarray[]: ";
        for (int i = 0; i < N; ++i)
        {
            if (_fdarray[i].fd == defaultfd)
            {
                continue;
            }
            std::cout << _fdarray[i].fd << " ";
        }
        std::cout << std::endl;
    }

    ~SelectServer()
    {
        _listensock.Close();
    }

private:
    uint16_t _port;
    Sock _listensock;
    type_t _fdarray[N]; // 用来管理所有的fd
    std::unordered_map<std::string, std::string> _dictionary;
};